<?php

class ImageInstance
{
    private $_filePath;
    private $_isImage = false;
    private $_imageDetails = array();
    private $_errorMessage = '';
    private $_isError = false;
    private $_realFileExtension;
    private $_supportedImageTypes = array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);
    
    public function __construct($filePath)
    {
        $this->_filePath = $filePath;

        if (!is_file($this->_filePath))
        {
            $this->SetError(Mozg_Lang::_('FILE_NOT_FOUND'));
            return;
        }
        if (!is_readable($this->_filePath))
        {
            $this->SetError(Mozg_Lang::_('FILE_NOT_READABLE'));
            return;
        }

        $this->_imageDetails = getimagesize($this->_filePath);
        
        if (!$this->_imageDetails)
        {
            $this->SetError(Mozg_Lang::_('INVALID_IMAGE_FORMAT'));
            return;
        }

        $this->_imageDetails['filesize'] = filesize($this->_filePath);
        
		if ($this->IsSupportedFormat())
		{
			$this->_isImage = true;
		}
		else
		{
            $this->SetError(Mozg_Lang::_('UNSUPPORTED_IMAGE_FORMAT'));
            return;
		}
    }

    public function GetImageInfo()
    {
        return $this->_imageDetails;
    }
    
    public function GetRealExtension()
    {
        if (!$this->IsImage()) throw new Mozg_Exception_System(Mozg_Lang::_('INVALID_IMAGE_FORMAT'));
        
        switch ($this->_imageDetails[2])
        {
            case IMAGETYPE_GIF:
                return 'gif';
                break;
            case IMAGETYPE_JPEG:
                return 'jpg';
                break;
            case IMAGETYPE_PNG:
                return 'png';
                break;
        }
    }
    
    private function IsSupportedFormat()
    {
        return in_array($this->_imageDetails[2], $this->_supportedImageTypes);
    }
    
    private function SetError($message)
    {
        $this->_errorMessage = $message;
        $this->_isError = true;
    }

    public function IsError()
    {
        return $this->_isError;
    }

    public function GetError()
    {
        return $this->_errorMessage;
    }
    
    public function IsImage()
    {
        return $this->_isImage;
    }
    
	private function FindSharp($orig, $final)
	{
		$final	= $final * (750.0 / $orig);
		$a		= 52;
		$b		= -0.27810650887573124;
		$c		= .00047337278106508946;
		
		$result = $a + $b * $final + $c * $final * $final;
		
		return max(round($result), 0);
	}

	public function copy($dstFilePath)
	{
        if (!$this->IsImage()) throw new Mozg_Exception_System(Mozg_Lang::_('INVALID_IMAGE_FORMAT'));
        
        $fileInfo = pathinfo($dstFilePath);

        if (!Mozg::Lib('dir')->Create($fileInfo['dirname']))
        {
            $this->SetError(Mozg_Lang::_('UNABLE_CREATE_DIRECTORY'));
            return '';
        }
        
        $newFileName = $fileInfo['basename'];
        
        $newFilePath = trim($fileInfo['dirname'], '.'). '/' .$newFileName;
		copy($this->_filePath, $newFilePath);
		@chmod($newFilePath, _CHMOD_FILE);

		return $newFileName;
	}
	
    public function Resize($path, $fileName, $maxSize = array(), $color = false, $quality = false, $saveFullImage = true, $watermark = false)
    {
        if (!$this->IsImage()) throw new Mozg_Exception_System(Mozg_Lang::_('INVALID_IMAGE_FORMAT'));
        
        if (!$quality) $quality = 90;
        
        //$fileName = pathinfo($fileName, PATHINFO_FILENAME);
        
        $path = rtrim($path, '/');
        if (strlen($path)) $path .= '/';
        
        if ('/' != substr($path, -1)) $path .= '/';
        
        $resizedImageFileName = $fileName;
        
		// Get the size and MIME type of the requested image
		$size	= $this->_imageDetails;

		$width			= max($size[0], 1);
		$height			= max($size[1], 1);

		$maxWidth = $maxSize[0] ? $maxSize[0] : 100;
		$maxHeight = $maxSize[1] ? $maxSize[1] : 100;

		if ($color) $color = preg_replace('/[^0-9a-fA-F]/', '', (string)$color);

		// If we don't have a max width or max height, OR the image is smaller than both
		// we do not want to resize it, so we simply output the original image and exit
		if (!$color && ($maxWidth >= $width) && ($maxHeight >= $height) && !$watermark)
		{
			return $this->copy($path.$fileName);
		}

		// Ratio cropping
		$offsetX	= 0;
		$offsetY	= 0;

		$cutVertical = ($width/$height) > ($maxWidth/$maxHeight);
		if ($saveFullImage) $cutVertical = !$cutVertical;

		if ($cutVertical)
		{
			$tnHeight = min($maxHeight, $height);
			
			$ratio = $tnHeight/$height;
			$tnWidth = ceil($ratio*$width);
		}
		else
		{
			$tnWidth = min($maxWidth, $width);
			
			$ratio = $tnWidth/$width;
			$tnHeight = ceil($ratio*$height);
		}

		list($startX, $canvasWidth) = $this->GetCanvasSize($tnWidth, $maxWidth);
		list($startY, $canvasHeight) = $this->GetCanvasSize($tnHeight, $maxHeight);
		
		// Set up a blank canvas for our resized image (destination)
		$dst	= imagecreatetruecolor($canvasWidth, $canvasHeight);

		// Read in the original image
		$src = $this->GetImageInstance();
		
		switch ($this->_imageDetails[2])
		{
			case IMAGETYPE_GIF:
				$outputFunction		= 'ImageGif';
				$doSharpen			= FALSE;
				$quality			= round(10 - ($quality / 10));
			break;
			
			case IMAGETYPE_PNG:
				$outputFunction		= 'ImagePng';
				$doSharpen			= FALSE;
				$quality			= round(10 - ($quality / 10)); // PNG needs a compression level of 0 (no compression) through 9
			break;
			
			default:
				$outputFunction	 	= 'ImageJpeg';
				$doSharpen			= TRUE;
			break;
		}

		if (in_array($this->_imageDetails[2], array(IMAGETYPE_GIF, IMAGETYPE_PNG)))
		{
			if (!$color)
			{
				// If this is a GIF or a PNG, we need to set up transparency
				imagealphablending($dst, false);
				imagesavealpha($dst, true);
			}
			else
			{
				// Fill the background with the specified color for matting purposes
				if ($color[0] == '#')
					$color = substr($color, 1);
				
				$background	= FALSE;
				
				if (strlen($color) == 6)
					$background	= imagecolorallocate($dst, hexdec($color[0].$color[1]), hexdec($color[2].$color[3]), hexdec($color[4].$color[5]));
				else if (strlen($color) == 3)
					$background	= imagecolorallocate($dst, hexdec($color[0].$color[0]), hexdec($color[1].$color[1]), hexdec($color[2].$color[2]));
				if ($background)
					imagefill($dst, 0, 0, $background);
			}
		}

		// Resample the original image into the resized canvas we set up earlier
		ImageCopyResampled($dst, $src, $startX, $startY, $offsetX, $offsetY, $tnWidth, $tnHeight, $width, $height);

		if ($doSharpen && function_exists('imageconvolution'))
		{
			// Sharpen the image based on two things:
			//	(1) the difference between the original size and the final size
			//	(2) the final size
			$sharpness	= $this->FindSharp($width, $tnWidth);
			
			$sharpenMatrix	= array(
				array(-1, -2, -1),
				array(-2, $sharpness + 12, -2),
				array(-1, -2, -1)
			);
			$divisor		= $sharpness;
			$offset			= 0;
			imageconvolution($dst, $sharpenMatrix, $divisor, $offset);
		}

		if ($watermark)
		{
			$dst = $this->SetWaterMark($dst, $canvasWidth, $canvasHeight);
		}
		
		// Write the resized image to the cache
		$outputFunction($dst, $path.$resizedImageFileName, $quality);
		@chmod($path.$resizedImageFileName, _CHMOD_FILE);
		
		// Clean up the memory
		$this->DestroyImageInstance($src);
		$this->DestroyImageInstance($dst);

        return $resizedImageFileName;
    }
    
    public function GetImageInstance()
    {
    	switch ($this->_imageDetails[2])
		{
			case IMAGETYPE_GIF:
				$creationFunction	= 'ImageCreateFromGif';
			break;
			
			case IMAGETYPE_PNG:
				$creationFunction	= 'ImageCreateFromPng';
			break;
			
			default:
				$creationFunction	= 'ImageCreateFromJpeg';
			break;
		}
		
		return $creationFunction($this->_filePath);
    }
    
    public function DestroyImageInstance($dstImage)
    {
    	ImageDestroy($dstImage);
    }
    
    private function SetWaterMark($dstImage, $dstWidth, $dstHeight)
    {
		$watermarkFile = Mozg_Cfg::Get('global/media')->GetWatermark();
		if (!$watermarkFile['file'] || !is_file(_ROOT_PATH.$watermarkFile['file'])) return $dstImage;

		$waterImg = new self(_ROOT_PATH.$watermarkFile['file']);
		if (!$waterImg->IsImage()) return $dstImage;
		
		$waterDst = $waterImg->GetImageInstance();
		
		$waterWidth = imagesx($waterDst);
		$waterHeight = imagesy($waterDst);
		
		$destX = $dstWidth - $waterWidth;
		$destY = $dstHeight - $waterHeight;
		
		$this->imagecopymerge_alpha($dstImage, $waterDst, $destX, $destY, 0, 0, $waterWidth, $waterHeight);
		$this->DestroyImageInstance($waterDst);
		
		return $dstImage;
    }
    
    function imagecopymerge_alpha(&$dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct = 0)
    {
        $opacity = $pct;
        // getting the watermark width
        $w = imagesx($src_im);
        // getting the watermark height
        $h = imagesy($src_im);
         
        // creating a cut resource
        $cut = imagecreatetruecolor($src_w, $src_h);
        // copying that section of the background to the cut
        imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h);
        // inverting the opacity
        $opacity = 100 - $opacity;
         
        // placing the watermark now
        imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h);
        imagecopymerge($dst_im, $cut, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $opacity);
    }
    
    private function GetCanvasSize($realSize, $maxSize)
    {
    	if ($realSize > $maxSize)
		{
			$canvasSize = $maxSize;
			$startPos = -ceil(($realSize - $maxSize)/2);
		}
		else
		{
			$canvasSize = $realSize;
			$startPos = 0;
		}

		return array($startPos, $canvasSize);
    }
}